

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;

import javax.rmi.CORBA.Util;

public class Station_Base {

	// ressources 
	public int total_dispo_trame;

	public int total_dispo_RT;
	public int total_nb_data_RT;
	public int total_nb_data_BE;
	public int nb_client_SRC;

	public int MIN_curseur=6;
	public int MAX_curseur=28;

	public boolean reject_all_clients; // rejete toutes les demandes

	private int cpt = 0;

	HashMap<Integer, Integer> connected_clients_dl_RT;
	HashMap<Integer, Integer> connected_clients_ul_RT;

	HashMap<Integer, Integer> connected_clients_dl_BE;
	HashMap<Integer, Integer> connected_clients_ul_BE;

	public boolean alert_trame_pleine;
//===========================================================================
	public Station_Base(){

		Utils.pos_curseur = this.MIN_curseur;
		// ressource
		this.reject_all_clients=false;

		this.connected_clients_dl_RT = new HashMap<Integer, Integer>(); // hashmap des connections actives en dl RT
		this.connected_clients_ul_RT = new HashMap<Integer, Integer>(); // hashmap des connections actives en ul

		this.connected_clients_dl_BE = new HashMap<Integer, Integer>(); // hashmap des connections actives en dl BE
		this.connected_clients_ul_BE = new HashMap<Integer, Integer>(); // hashmap des connections actives en ul
		
		this.check_auto_tradeoff();
		
	}
//===========================================================================
	public Trame send_trame(){

		Trame tr = new Trame();
		return tr;
	}
//===========================================================================
	public void check_auto_tradeoff(){ // check si on est en mode auto pour le curseur
		
		if(Utils.user_curseur!=0){
			
			Utils.pos_curseur=Utils.user_curseur; 
		}
	}
	//===========================================================================	
	private void add_connected_client_and_make_debit_RT(int id_conn, int servclasse, int location){ // put les clients dans la table active, et creer le debit dl des clients

		int client_debit_dl = this.make_debit_dl_RT(location);

		int client_debit_ul = this.make_debit_ul(client_debit_dl);

		this.connected_clients_dl_RT.put(id_conn, client_debit_dl);
		this.connected_clients_ul_RT.put(id_conn, client_debit_ul);
	}
	//===========================================================================
	public void add_connected_client_BE(int id_conn){

		this.connected_clients_dl_BE.put(id_conn, 0);
	}
//===========================================================================
	public void make_debit_dl_ul_BE(){ // calcul et inscris dans les map de connexions le debit ul et dl pour les BE

		int nb_BE= this.connected_clients_dl_BE.size();

		if(nb_BE!=0){

			int debit_dl_BE = ((this.total_dispo_trame - this.total_nb_data_RT)/nb_BE); // débit equitable entre tt les BE

			if(debit_dl_BE<1)
				debit_dl_BE=1; // On conserve un debit minimal quand la trame est pleine
			
			if(debit_dl_BE<1)
				System.exit(1);
			
			int debit_ul_BE = this.make_debit_ul(debit_dl_BE);

			if (Utils.log==1)
			try {Utils.writeF("total dispo trame "+this.total_dispo_trame + " total nb data RT "+this.total_nb_data_RT + "nb BE "+nb_BE , "console");} 
			catch (IOException e) {e.printStackTrace();}
			////System.out.println("total dispo trame "+this.total_dispo_trame + " total nb data RT "+this.total_nb_data_RT + "nb BE "+nb_BE);
			if (Utils.log==1)
			try {Utils.writeF("Debit BE "+debit_dl_BE , "console");} 
			catch (IOException e) {e.printStackTrace();}
			//System.out.println("Debit BE "+debit_dl_BE);

			Set cles = this.connected_clients_dl_BE.keySet();
			Iterator it = cles.iterator();
			while (it.hasNext()){
				Integer cle = (Integer) it.next(); 

				this.connected_clients_dl_BE.put(cle, debit_dl_BE);
				this.connected_clients_ul_BE.put(cle, debit_ul_BE);
			}
		}
	}
//===========================================================================
	private int make_debit_ul(int debit_dl ){ // creer un debit ul a - 30% du debit dl

		int debit_ul=debit_dl-((debit_dl*50)/100);

		return debit_ul;
	}
//===========================================================================
	private int make_debit_dl_RT(int dist){ // donne le debit des clients en fonction de leur distance et de leur classe de service

		if(dist>5){
			Utils.random(3,4);
		}
		return Utils.random(4,5);
	}
//===========================================================================
	public void write_fch_and_actives_connections(Trame tr) { // Specs : http://wirelessman.org/tga/contrib/C80216a-02_33.pdf
	
		////System.out.println("SB : writing UL map & FCH");
		if (Utils.log==1)
			try {Utils.writeF("SB : writing UL map & FCH" , "console");} 
			catch (IOException e) {e.printStackTrace();}
		
		int nb_Data_to_transmit;
		int id_conn,serv_class, loc;

		for (int i=0; i< tr.Ranging_Sub_Channel.length; i++) {	// Pour chaque slot...
			for (int j=0; j< tr.Ranging_Sub_Channel[i].length && tr.Ranging_Sub_Channel[i][j]!=null; j++) {	// Pour chaque client du slot...

				//System.out.println("rsc ici --> "+tr.Ranging_Sub_Channel[i][j]);

				StringTokenizer tok = new StringTokenizer(tr.Ranging_Sub_Channel[i][j], ";");	// On découpe
				id_conn = Integer.parseInt(tok.nextToken());
				serv_class = Integer.parseInt(tok.nextToken());
				loc = Integer.parseInt(tok.nextToken());

				if (Utils.collision_by_trame.contains(id_conn)){	// Si le client a eu une collision
					tr.FCH.add(id_conn);	// On inscrit le client qui a eu une collision dans FCH
					if (Utils.log==1)
						try {Utils.writeF("adddddddddddddddddddddddin" , "console");} 
						catch (IOException e) {e.printStackTrace();}
						////System.out.println("adddddddddddddddddddddddin");
				}
				else { // si le client n'est PAS en collision ON accepte

					if(this.reject_all_clients==true)
						Utils.nb_rejets++;

					if(serv_class==2 && this.reject_all_clients==false){ // si la classe est RT et qu on est en accept mode
						//add le client dans la map des connexion actives
						this.add_connected_client_and_make_debit_RT(id_conn, serv_class, loc);
						Statistique.nb_clients_actif++;
						this.nb_client_SRC++; // ajout dans le nombre de client dans SRC
					}

					if(serv_class==1 && this.reject_all_clients==false){ // si la classe est BE 

						this.add_connected_client_BE(id_conn);
						Statistique.nb_clients_actif++;
						this.nb_client_SRC++;
					}
				}
			}
		}
	}
//===========================================================================
	public void write_DL_UL_Map(Trame tr) { 

		if (Utils.log==1)
			try {Utils.writeF("SB : writing dl map .." , "console");} 
		catch (IOException e) {e.printStackTrace();}
		////System.out.println("SB : writing dl map ..");

		tr.DL_map.putAll(this.connected_clients_dl_RT);// ecrit dans dl map les id clients et leurs debit DL(en fonction de leur classe et loc)
		tr.UL_map.putAll(this.connected_clients_ul_RT);// ecrit dans ul map les id clients et leurs debit UL(en fonction de leur debit DL)

		tr.DL_map.putAll(this.connected_clients_dl_BE);// ecrit dans dl map les id clients et leurs debit BE
		tr.UL_map.putAll(this.connected_clients_ul_BE);// ecrit dans ul map les id clients et leurs debit UL(en fonction de leur debit DL)

	}
//===========================================================================
	public void raz_dl_ul_map_fch(Trame t1){

		t1.get_DL_map().clear();
		t1.get_UL_map().clear();
		t1.get_FCH().clear();
	}
//===========================================================================
	public void raz_ranging_sub_channel(Trame tr){

		String tab[][]= tr.Ranging_Sub_Channel;

		for(int i =0;i<tab.length;i++){
			for(int j =0 ; j<tab[i].length;j++){
				tab[i][j]=null ;
			}
		}
	}
//===========================================================================
	public int calcul_total_nb_data_RT(){ // calcul le total de donnees pour les RT

		int total_data=0;

		Set cles = this.connected_clients_dl_RT.keySet();
		Iterator it = cles.iterator();
		while (it.hasNext()){
			Integer cle = (Integer) it.next();
			Integer valeur = this.connected_clients_dl_RT.get(cle);

			total_data=total_data+valeur;
		}

		return total_data;
	}
//===========================================================================
	public int calcul_total_data_nb_BE(){ // calcul le total de donnees pour les BE

		int total_data=0;

		Set cles = this.connected_clients_dl_BE.keySet();
		Iterator it = cles.iterator();
		while (it.hasNext()){
			Integer cle = (Integer) it.next();
			Integer valeur = this.connected_clients_dl_BE.get(cle);

			total_data=total_data+valeur;
		}
		return total_data;
	}
//===========================================================================
	public void check_trame_full(){ // test si la trame est pleine et ne peut plus accepter de client 

		if (Utils.log==1)
			try {Utils.writeF("Check Trame" , "console");} 
		catch (IOException e) {e.printStackTrace();}
		////System.out.println("Check Trame");
		
		if(this.total_dispo_trame<=(this.total_nb_data_RT+this.total_nb_data_BE)){
			this.alert_trame_pleine=true;
			
			if (Utils.log==1)
			try {Utils.writeF("Alerte !!!!!!!!!!!!!!!!!!!!! trame pleine ." , "console");} 
			catch (IOException e) {e.printStackTrace();}
			////System.out.println("Alerte !!!!!!!!!!!!!!!!!!!!! trame pleine .");
			this.reject_all_clients=true; // rejette les clients
		}
		else {this.alert_trame_pleine=false;
		this.reject_all_clients=false; // ne rejette pas les clients 
		}

		if (Utils.log==1)
			try {Utils.writeF("Rejet client = "+this.reject_all_clients , "console");} 
		catch (IOException e) {e.printStackTrace();}
		////System.out.println("Rejet client = "+this.reject_all_clients);
	}
//===========================================================================
	public void calcul_ressources(){ // calcul des ressources dispo

		this.total_nb_data_RT=this.calcul_total_nb_data_RT();
		this.total_nb_data_BE=this.calcul_total_data_nb_BE();

		//this.calcul_total_dispo_trame();

		//this.check_trame_full();
	}
//===========================================================================
	public void flush_client(Trame t1){ // enlève les clients de la tabel des connexion actives.

		int id;
		for ( int i = 0; i < t1.ul_transfert_fini.size(); i++) {

			id = t1.ul_transfert_fini.get(i);
			this.connected_clients_dl_BE.remove(id);
			this.connected_clients_dl_RT.remove(id);
			this.connected_clients_ul_BE.remove(id);
			this.connected_clients_ul_RT.remove(id);
		}
		t1.ul_transfert_fini.clear();
	}
//===========================================================================
	public void min_curseur(){ // position min que devra avoir le curseur pour les clients deja connecte (BE et RT )

		int data_rt = this.total_nb_data_RT;
		int nb_be = this.connected_clients_dl_BE.size();

		this.MIN_curseur = ((data_rt+(nb_be*Utils.min_debit_BE))/Utils.nb_freq)+3;

		/*
		 * System.out.println("DEBUG******");

		System.out.println("Min curseur = "+this.MIN_curseur);
		System.out.println("total nb data RT = "+this.total_nb_data_RT);
		System.out.println("connected client BE . size = "+this.connected_clients_dl_BE.size());
		System.out.println("min debit utils "+Utils.min_debit_BE);
		System.out.println("nb freq "+Utils.nb_freq);
		System.out.println("//DEBUG******");
		 */
	}
//===========================================================================
	public void calcul_curseur(){ // calcul les tailles de dl et ul subframe (en nb de slots ) en fonction des clients dans DL et dans RSC

		this.total_nb_data_RT=this.calcul_total_nb_data_RT();
		this.total_nb_data_BE=this.calcul_total_data_nb_BE();

		this.calcul_total_dispo_trame();

		if(Utils.user_curseur==0){ // si le curseur est en mode Automatique

			this.min_curseur();

			int nb_client_DL=0; // nb de client connecte
			int nb_client_UL=0; // nb de demandes dans le Ranging sub channel

			nb_client_UL=this.nb_client_SRC; // nb client dans l'ul (demande d'acces au canal)
			nb_client_DL=((this.connected_clients_dl_BE.size())+(this.connected_clients_dl_RT.size())); // total des clients dans DL


			if(nb_client_DL > nb_client_UL){/////////

				if(nb_client_UL==0){
					Utils.pos_curseur= (Utils.pos_curseur + 2); // on augmente le curseur de 2 si 0 client dans RSC
				}
				else Utils.pos_curseur = Utils.pos_curseur + ((nb_client_DL/nb_client_UL)); // sinon au augmente le curseur du ratio entre nombre de client DL et UL
			}

			if(nb_client_UL > nb_client_DL){//////////

				if(nb_client_DL==0){
					Utils.pos_curseur = (Utils.pos_curseur - 2); // on diminue le curseur de 2 si 0 client dans DL
				}
				else Utils.pos_curseur = Utils.pos_curseur - ((nb_client_UL/nb_client_DL)); // sinon au augmente le curseur du ratio entre nombre de client DL et UL
			}

			this.nb_client_SRC=0; // on remet a zero le nb de client dans src pour la trame en cour 

			if(Utils.pos_curseur > this.MAX_curseur) // On controle que le calcul ne depasse pas les bornes min et max du curseur
				Utils.pos_curseur=this.MAX_curseur;

			if(Utils.pos_curseur < this.MIN_curseur)
				Utils.pos_curseur=this.MIN_curseur;

		}
		
		if (Utils.log==1)
			try {Utils.writeF("Position curseur = "+Utils.pos_curseur , "console");} 
		catch (IOException e) {e.printStackTrace();}
		//System.out.println("Position curseur = "+Utils.pos_curseur);

	}
//===========================================================================
	public void calcul_total_dispo_trame(){

		this.total_dispo_trame=(Utils.nb_freq*Utils.pos_curseur); //x frequences et x slots
		if (Utils.log==1)
			try {Utils.writeF("Total dispo trame calcul-----------------xxxxxxxx"+this.total_dispo_trame , "console");} 
		catch (IOException e) {e.printStackTrace();}
		////System.out.println("Total dispo trame calcul-----------------xxxxxxxx"+this.total_dispo_trame);
	}
//===========================================================================
	public void traitement_trame(Trame t1){

		if (Utils.log==1)
			try {Utils.writeF("SB traitement ." , "console");} 
			catch (IOException e) {e.printStackTrace();}
		//System.out.println("SB traitement .");

		this.flush_client(t1);	// enleve les clients qui n'ont plus rien a telecharger 

		//this.calcul_ressources();

		this.calcul_curseur(); // Calcul de la position du curseur 
		this.check_trame_full();
		this.write_fch_and_actives_connections(t1); // write ul map et fch 
		this.make_debit_dl_ul_BE();
		this.write_DL_UL_Map(t1);	
		this.calcul_ressources(); // Calcul les ressources encore disponibles (taille de la trame)

		if (Utils.log==1){
			try {Utils.writeF("DL_MAP --> "+ t1.get_DL_map().toString() , "console");} 
			catch (IOException e) {e.printStackTrace();}
			////System.out.println("DL_MAP --> "+ t1.get_DL_map().toString());

			try {Utils.writeF("UL_MAP --> "+ t1.get_UL_map().toString() , "console");} 
			catch (IOException e) {e.printStackTrace();}
			////System.out.println("UL_MAP --> "+ t1.get_UL_map().toString());
		}
	}
}// end class
